package com.example.user1.config;

import com.example.user1.util.User;
import com.example.user1.util.UserThreadLocal;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.codec.SerializationCodec;
import org.springframework.beans.BeanUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

import static com.example.user1.config.AutoConfig.PRIVATE_KEY;
import static com.example.user1.config.AutoConfig.RSA;

@WebFilter("/*")
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Slf4j
@RequiredArgsConstructor
public class LoginFilter implements Filter {
    private final LoginProperties loginProperties;
    private final RedissonClient redissonClient;
    private RSAPublicKey rsaPublicKey;
    private final static String PUBLIC_KEY = "public_key";

    private final static AntPathMatcher antpathmatcher = new AntPathMatcher();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("login filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws RuntimeException, IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        if (rsaPublicKey == null) {
            Optional.ofNullable(redissonClient.getMap(RSA, new SerializationCodec())).map(x -> (x.get(PUBLIC_KEY))).ifPresent(x -> {
                rsaPublicKey = (RSAPublicKey) x;
            });
            if (rsaPublicKey == null) {
                log.error("无权限");
                httpServletResponse.setStatus(401);
                return;
            }
        }
        //过滤白名单
        String uri = httpServletRequest.getRequestURI();
        if (loginProperties.getWhiteList().stream().anyMatch(x -> antpathmatcher.match(x, uri))) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        String tokenCookie = httpServletRequest.getHeader("Authorization");
        if (!StringUtils.hasText(tokenCookie)) {
            //未携带token
            log.error("无权限");
            httpServletResponse.setStatus(401);
            return;
        }

        //解析token
        try {
            SignedJWT jwt = SignedJWT.parse(tokenCookie);
            JWSVerifier verifier = new RSASSAVerifier(rsaPublicKey);
            if (!jwt.verify(verifier)) {
                log.error("无权限");
                httpServletResponse.setStatus(401);
//                httpServletResponse.sendRedirect("/user/login");
                return;
            }
            JWTClaimsSet claimsSet = jwt.getJWTClaimsSet();
            Date expirationTime = claimsSet.getExpirationTime();
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis > expirationTime.getTime()) {
                //token 已经过期了
                log.error("无权限");
                httpServletResponse.setStatus(401);
//                httpServletResponse.sendRedirect("/user/login");
                return;
            }
            if (currentTimeMillis > expirationTime.getTime() + 5 * 60 * 1000 && currentTimeMillis < expirationTime.getTime()) {
                //五分钟后即将过期
                httpServletResponse.setStatus(402);
                return;
            }
            User user = new User();
            Map<String, Object> claims = claimsSet.getClaims();
            user.setId((Long) claims.get("userId"));
            user.setUsername((String) claims.get("username"));
            UserThreadLocal.set(user);
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (ParseException | JOSEException | ServletException e) {
            log.error("failed to parse token", e);
            //token 非法token
            log.error("无权限");
            httpServletResponse.setStatus(401);
        } finally {
            UserThreadLocal.remove();
        }
    }
}
